package com.dd.keel.core.common.utils;

import java.util.Iterator;
import java.util.Map;
import java.util.Collections;
import java.util.List;
import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.ArrayList;
import java.util.Properties;
import java.lang.StringBuffer;

import java.nio.charset.Charset;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharsetEncoder;

/**
 * String util
 * @author Kevin.XU
 *
 */
public class StringUtil {
	
	private static Map<String,CharsetEncoder> encoders = new HashMap<String,CharsetEncoder>();
	
	public static String padStringWithFixedLength(String str,int fixedLength)
	{
		if(str==null) return null;
		if( fixedLength<0 || fixedLength<=str.length() ) return str;
		StringBuilder sb = new StringBuilder();
		sb.append(str);
		int count = fixedLength - str.length();
		while(count-->0)
		{
			sb.append(" ");
		}
		return sb.toString();
	}
	
	public static String padStringWithFixedLength(String str,int fixedLength,char padChar )
	{
		if(str==null) return null;
		if( fixedLength<0 || fixedLength<=str.length() ) return str;
		int count = fixedLength - str.length();
		StringBuilder sb = new StringBuilder();
		while(count-->0)
		{
			sb.append(padChar);
		}
		sb.append(str);
		return sb.toString();
	}
	
	public static String[] getArrayFromString(String str, String sep)
	{
		if(str==null || sep==null) return null;
		StringTokenizer st = new StringTokenizer(str,sep);
		ArrayList<String> list = new ArrayList<String>();
		while(st.hasMoreTokens())
		{
			String token = st.nextToken().trim();
			if(!token.equals(""))
			{
				list.add(token);
			}
		}
		if(list.isEmpty()) return null;
		String[] temp = new String[list.size()];
		for(int i=0; i<list.size(); i++)
		{
			temp[i] = list.get(i);
		}
		return temp;
	}
	
	public static String replaceAll(String expression,String oldString, String newString) 
	{
		String replacedExpression = expression;
		if (replacedExpression != null) {
			int charCtr = 0;
			int oldStringIndex = replacedExpression.indexOf(oldString, charCtr);
			while (oldStringIndex > -1) {
				// Remove the old string from the expression.
				StringBuffer buffer = new StringBuffer(replacedExpression.substring(0, oldStringIndex)+ replacedExpression.substring(oldStringIndex+ oldString.length()));
				// Insert the new string into the expression.
				buffer.insert(oldStringIndex, newString);
				replacedExpression = buffer.toString();
				charCtr = oldStringIndex + newString.length();
				// Determine if we need to continue to search.
				if (charCtr < replacedExpression.length()) {
					oldStringIndex = replacedExpression.indexOf(oldString,charCtr);
				} else {
					oldStringIndex = -1;
				}
			}
		}
		return replacedExpression;
	}
	
	/**
	 * When JDK 1.5, String's 'getBytes' method is implemented by NIO Byte buffer API, and use many caches.
	 * So, method one is almost same quick as method two at the performance.
	 */
	public static byte[] encodeStringToBytes(String source,String charsetName) throws Exception
	{
		/**
		 * method one.
		 */
		/*CharsetEncoder encoder = null;
		if(!encoders.containsKey(charsetName)){
			encoder = Charset.forName(charsetName).newEncoder(); 
			encoders.put(charsetName, encoder);
		}else{
			encoder = encoders.get(charsetName);
		}
		synchronized(encoder)
		{
			encoder.reset();
			ByteBuffer bytes = encoder.encode(CharBuffer.wrap(source.toCharArray()));
			return bytes.array();
		}*/
		/**
		 * method two.
		 */
		return source.getBytes(charsetName);
	}
	
	public static boolean isEmptyCharByte(byte b)
	{
		if( (b>=0x00 && b<=0x2F) || (b>=0x3A && b<=0x40) || (b>=0x5B && b<=0x60) || (b>=0x7B && b<=0x7F)  )
		{
			return true;
		}
		//if(b==0x09 || b==0x20 || b==0x0D || b==0x0A) return true;
		return false;
	}
	
	/**
	 * Split source string into multi parts , every part's size is shorter than max length.
	 * The performance of this function is very important.
	 * NOTICE: This arithmetic is appropriate when the source consist of only english words or chinese 'short' sentence.
	 * Performance report:
	 * 5k    ->  cost time is 1  milli seconds
	 * 50K   ->  cost time is 3  milli seconds
	 * 250K  ->  cost time is 10 milli seconds
	 * 500K  ->  cost time is 14 milli seconds
	 * @param source
	 * @param charsetName
	 * @param maxLength
	 * @return
	 */
	public static List<String> split(String source,String charsetName,int maxLength) throws Exception
	{
		//StopWatch tempsw = new StopWatch(StopWatch.NANO_TIME_UNIT);
		//StopWatch sw = new StopWatch(StopWatch.NANO_TIME_UNIT);
		if(source==null) return null;
		if(charsetName==null) charsetName = "UTF-8";
		List<String> part_list = new ArrayList<String>();
		//sw.start();
		byte[] whole_byte_array = encodeStringToBytes(source,charsetName);
		//sw.stop();
		//System.out.println("cost1="+sw.cost());
		int whole_length = whole_byte_array.length;
		if(whole_length <= maxLength)
		{
			part_list.add(source);
			return part_list;
		}
		int start_offset = 0;
		int split_pos = 0;
		int part_real_length = 0;
		//sw.restart();
		do{
			split_pos = start_offset + maxLength - 1;
			if( split_pos  >= whole_length - 1 )
			{
				part_real_length = whole_length - 1 - start_offset + 1 ;
				//tempsw.start();
				part_list.add( new String(whole_byte_array,start_offset,part_real_length,charsetName) );
				//tempsw.stop();
				//System.out.println("cost2="+tempsw.cost());
				break;
			}
			byte tempByte = whole_byte_array[split_pos];
			if( isEmptyCharByte(tempByte) ) 
			{
				part_real_length = maxLength;
				part_list.add( new String(whole_byte_array,start_offset,part_real_length,charsetName) );
				start_offset = split_pos + 1;
			}
			else
			{
				if( isEmptyCharByte( whole_byte_array[split_pos+1] ) )
				{
					part_real_length = maxLength;
					part_list.add( new String(whole_byte_array,start_offset,part_real_length,charsetName) );
					start_offset = split_pos + 1;
				}
				else
				{
					int last_pos = split_pos-1;
					do{
						tempByte = whole_byte_array[last_pos];
						if( isEmptyCharByte(tempByte) )
						{
							break;
						}
						--last_pos;
					}while( last_pos > start_offset );
					part_real_length = last_pos - start_offset + 1;
					part_list.add( new String(whole_byte_array,start_offset,part_real_length,charsetName) );
					start_offset = last_pos + 1;
				}
			}
		}while( start_offset <= whole_length-1 );
		//sw.stop();
		//System.out.println("cost10="+sw.cost());
		return part_list;
	}
	
	public static boolean isNumber(String numberString, int maxLength)
	{
		if(numberString==null || (maxLength>0 && numberString.length()!=maxLength) ) return false;
		for(int i=0; i<numberString.length(); ++i)
		{
			if( !Character.isDigit(numberString.charAt(i)) ) return false;
		}
		return true;
	}
	
	public static boolean isInScope(String valueString, String[] scope)
	{
		if(valueString==null) return false;
		boolean match = false;
		for(int i=0; i<scope.length; ++i)
		{
			if(valueString.equals(scope[i]))
			{
				match = true;
				break;
			}
		}
		return match;
	}
	
	public static boolean isNumber(String numberString, long minValue, long maxValue)
	{
    	long temp = 0L;
    	try{
    		temp = Long.parseLong(numberString);
    		if( temp>=minValue && temp<=maxValue )
    		{
    			return true;
    		}
    		else
    		{
    			return false;
    		}
    	}catch(Exception ex){
    		return false;
    	}
	}
	
	public static boolean isPositiveNumber(String numberString)
	{
    	long temp = 0L;
    	try{
    		temp = Long.parseLong(numberString);
    		if( temp>=0 )
    		{
    			return true;
    		}
    		else
    		{
    			return false;
    		}
    	}catch(Exception ex){
    		return false;
    	}
	}
	
	/**
	 * smsno is like '1120203225|1230000'
	 * @param smsno
	 * @return
	 */
	public static long[] transferSmsnoToSeqNum(String smsno) throws Exception
	{
		if(smsno==null) throw new Exception("smsno is null");
		smsno = smsno.trim();
		String[] array = smsno.split("\\|");
		if(array.length!=2) throw new Exception("smsno ["+smsno+"] is invalid");
		long[] resultArray = new long[2];
		resultArray[0] = DateUtil.getCommandDatetime(array[0]);
		resultArray[1] = Long.parseLong(array[1].trim());
		return resultArray;
	}
	
	/**
	 * seqNum 's size must be 2
	 * @param seqNum
	 * @return like '0020203225|1230000'
	 * @throws Exception
	 */
	public static String transferSeqNumToSmsno(long[] seqNum) throws Exception
	{
		if(seqNum.length!=2) throw new Exception("seqNum's size ["+seqNum.length+"] is not 2");
		StringBuffer smsno = new StringBuffer();
		String temp = null;
		temp = seqNum[0]+"";
		while(temp.length()!=10)
		{
			temp = "0"+temp;
		}
		smsno.append(temp).append('|');
		smsno.append(seqNum[1]);
		return smsno.toString();
	}
	
	/**
	 * handle '1*60*60' etc
	 * @param value
	 * @return
	 */
	public static long getLongValue(String value)
	{
		if(value==null) return 0;
		value = value.trim();
		if(value.equals(""))
		{
			return 0;
		}
		long longvalue = 1L;
		String[] array = value.split("\\*");
		for(int i=0; i<array.length; ++i)
		{
			try{
				longvalue = longvalue * Long.parseLong(array[i].trim());
			}catch(Exception ex){}
		}
		return longvalue;
	}
	
	public static int getIntValue(String value)
	{
		if(value==null) return 0;
		value = value.trim();
		if(value.equals(""))
		{
			return 0;
		}
		int longvalue = 1;
		String[] array = value.split("\\*");
		for(int i=0; i<array.length; ++i)
		{
			try{
				longvalue = longvalue * Integer.parseInt(array[i].trim());
			}catch(Exception ex){}
		}
		return longvalue;
	}
	
	/**
	 * 返回一个3元数组
	 * index 0 ->  目标主机
	 * index 1 ->  目标端口
	 * index 2 ->  URI
	 * @param url
	 * @return
	 */
	public static String[] parseUrl(String url)
	{
		if(url==null) return null;
		url = url.trim();
		int defaultPort = 80;
		int protocolType = 1;  // 1 means http, 2 means https
		if(url.startsWith("https"))
		{
			defaultPort = 443;
			protocolType = 2;
		}
		if(protocolType==1)
		{
			url = url.replaceFirst("http://", "");
		}else{
			url = url.replaceFirst("https://", "");
		}
		int pos = -1;
		pos = url.indexOf('/');
		if(pos!=-1)
		{
			String[] result = new String[3];
			String target = url.substring(0, pos);
			String[] temp = target.split(":");
			if(temp.length==2)
			{
				result[0] = temp[0];
				result[1] = temp[1];
			}
			else
			{
				result[0] = target;
				result[1] = defaultPort+"";
			}
			String uri = url.substring(pos);
			result[2] = uri;
			return result;
		}
		else
		{
			return null;
		}
	}
	
}
